home *** CD-ROM | disk | FTP | other *** search
- /************************************************************************
- 2-Dimensional Planetary Simulation for Turbo C
- by C. Daniel Hassell
-
- Adapted from "Force-Based Simulations" by Todd King in Dr. Dobb's Journal,
- September 1989. The original code was in C++, and operated in character
- mode with no run-time controls. (No criticism of Mr. King intended; he only
- wanted to demo the power of C++ in a short program. I don't own C++ and
- don't want to, but I liked his simulation concept.)
-
- The program simulates the movements of a set of bodies affecting each other
- by mutual gravitational pull. The simulation is quite realistic, except for
- its 2-dimentional nature. The masses are in Kg and the velocities are in
- Km/Hr. For example, the entire list of 9 planets and their moons could be
- included and shown orbiting the Sun.
-
- The view space is defined by extent_x and extent_y. Zooming out doubles the
- values of these variables, while zooming in halves them. A circle is drawn
- for each planet within the view space with a radius related to the cube root
- of the mass of the planet. Panning and zooming in or out is allowed using
- the PC cursor movement keys. Panning in any direction occurs in 1/4-screen
- increments.
-
- Each loop through the simulation equals one hour. The time is shown in the
- upper left corner of the screen as days:hours. The speed of the simulation
- is dependent on the speed of the system; it can be slowed down by adding a
- delay() function somewhere but not easily cranked up faster. On my old
- PC/AT with a math chip, it takes a little over a second to tick off one
- simulated day when there are two bodies in the system. Each added planet
- slows it down noticably, since the number of FLOPS required is a multiple
- of n * ( n - 1 ), where n = the number of bodies.
-
- The initial set of planets must now be entered as constants in the main()
- function, but I hope some bright person designs a better user interface.
- (Turbo C's easy environment can make one lazy that way.) A new "planet X",
- defined in the big_bang() function, can be dropped in by pressing 'x'.
-
- Collisions between planets result in the absorption of the two into one.
- The resulting body has a mass and velocity vector equal to the sum of those
- of the two separate bodies. The total number of planets existing at one
- time is limited to POOL_LIMIT. On a color display, the planets will
- leave a trail of blue so that it is easy to see where they have been, but
- the trails are lost when the pan or zoom functions are used. On a monochrome
- monitor, no trail is left.
-
- The simulation is now set up as it was published by Todd King, showing the
- Moon orbiting the Earth. A "planet X" can be introduced at any time to
- demonstrate the disturbing effect a passing body could have on the orbits
- of the Earth and the Moon. Any hypothetical orbital system can be written
- in, however. An interesting arrangement is to set four identical planets
- at the corners of a perfect square in orbit around their common center of
- gravity. No matter how exact the initial placement, this system is highly
- unstable and will become chaotic over time.
-
- Comments are welcome at [72017,2466].
-
- C. Daniel Hassell, September 13, 1989.
-
- ************************************************************************/
- #include <dos.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <conio.h>
- #include <math.h>
- #include <time.h>
- #include <graphics.h>
-
- #define G -6.67e-11 /* the gravitational constant */
- #define SECS_PER_TIC 3600L /* 1 Hour, originally = 86400 or 1 day */
- #define ESC 27
- #define POOL_LIMIT 20 /* limit on the number of bodies */
- #define RADIUS_FUDGE 50 /* factor used to set size of circles */
-
- typedef struct {
- double x;
- double y;
- } VECTOR_2D; /* vector typedef for location and velocity */
-
- typedef struct {
- VECTOR_2D world; /* location in Km */
- VECTOR_2D velocity; /* velocity in Km/Hr */
- double mass; /* mass in Kg */
- double gmass; /* G * mass */
- int radius; /* Radius of circle for this body */
- } BODY;
-
- /* Global declarations */
- unsigned body_cnt; /* BODY count (pardon the expression) */
- BODY *body_pool[POOL_LIMIT]; /* array of pointers to BODYs */
- int GraphDriver; /* the graphics device driver */
- int GraphMode; /* the graphics mode value */
- int MaxX, MaxY; /* the maximum resolution of the screen */
- int MaxColor; /* the maximum color # available */
- int ErrorCode; /* reports any graphics errors */
- int height; /* height of default text */
- double extent_x = 15e8;
- double extent_y = 15e8; /* initial size of view space in Km */
- int trail_color; /* color of trail behind planets */
-
- /* Function prototypes */
- void initialize(void);
- void drawborder(void);
- int gprintf(int xloc, int yloc, char *fmt, ... );
- void set_mass( BODY *b, double m );
- void set_velocity( BODY *b, double x, double y );
- void set_position( BODY *b, double x, double y );
- void apply_force( BODY *b, BODY *from );
- void update( BODY *b );
- void print_tick( void );
- void convert( VECTOR_2D *world, VECTOR_2D *screen );
- void collision( BODY *b, BODY *from );
- void make_planet( double mass, double px, double py, double vx, double vy );
- char move_view( void );
- void big_bang( void );
-
-
- void initialize(void)
- { /* Initializes the graphics system and reports any errors */
- GraphDriver = DETECT; /* Request auto-detection */
- initgraph( &GraphDriver, &GraphMode, "" );
- ErrorCode = graphresult(); /* Read result of initialization*/
- if( ErrorCode != grOk ){ /* Error occured during init */
- printf(" Graphics System Error: %s\n", grapherrormsg( ErrorCode ) );
- exit( 1 );
- }
- MaxColor = getmaxcolor(); /* Read maximum number of colors*/
- MaxX = getmaxx();
- MaxY = getmaxy(); /* Read size of screen */
- return;
- }
-
- void drawborder(void)
- { /* Draw a solid line border and establish text settings. */
- cleardevice(); /* Clear graphics screen */
- height = textheight( "H" ); /* Get basic text height */
- setcolor( MaxColor );
- setlinestyle( SOLID_LINE, 0, NORM_WIDTH );
- rectangle( 0, height+4, MaxX, MaxY-(height+4) ); /* Draw border */
- settextjustify( CENTER_TEXT, TOP_TEXT );
- gprintf( MaxX/2, 1, "Planetary Simulation" );
- settextjustify( CENTER_TEXT, BOTTOM_TEXT );
- gprintf( MaxX/2, MaxY, "Press ESC to quit, PgUp or PgDn to zoom, X for new planet, or %c%c%c%c to pan.", 24, 25, 26, 27 );
- settextjustify( LEFT_TEXT, TOP_TEXT );
- if ( MaxColor > 2 ) /* set color of trail left by planets */
- trail_color = BLUE; /* blue trail if colors available */
- else
- trail_color = BLACK; /* ... else no trail */
- return;
- }
-
- int gprintf( int xloc, int yloc, char *fmt, ... )
- { /* Used like PRINTF except the output is sent to the
- screen in graphics mode at the specified co-ordinate. */
- va_list argptr; /* Argument list pointer */
- char str[140]; /* Buffer to build sting into */
- int cnt; /* Result of SPRINTF for return */
-
- va_start( argptr, format ); /* initialize va_ functions */
- cnt = vsprintf( str, fmt, argptr ); /* prints string to buffer */
- outtextxy( xloc, yloc, str ); /* Send string in graphics mode */
- va_end( argptr ); /* Close va_ functions */
- return( cnt ); /* Return the conversion count */
- }
-
- void set_mass( BODY *b, double m )
- { /* radius determined by cube root of mass */
- b->mass = m;
- b->gmass = G * m;
- b->radius = max( 1, (int)( pow(b->mass, (1.0/3.0))/extent_x*RADIUS_FUDGE ));
- return;
- }
-
- void set_velocity( BODY *b, double x, double y )
- {
- b->velocity.x = x;
- b->velocity.y = y;
- return;
- }
-
- void set_position( BODY *b, double x, double y )
- {
- b->world.x = x;
- b->world.y = y;
- return;
- }
-
- void collision( BODY *b, BODY *from )
- { /* Simulates a collision by absorbing from into b */
- double x, y, new_mass;
- int i;
- VECTOR_2D s;
-
- new_mass = b->mass + from->mass; /* calculate combined mass & speed */
- x = ( b->velocity.x * b->mass + from->velocity.x * from->mass ) / new_mass;
- y = ( b->velocity.y * b->mass + from->velocity.y * from->mass ) / new_mass;
- convert( &from->world, &s );
- setcolor( trail_color );
- circle( (int)s.x, (int)s.y, from->radius ); /* removes from from screen */
- convert( &b->world, &s );
- circle( (int)s.x, (int)s.y, b->radius ); /* removes b from screen */
- set_mass( b, new_mass );
- set_velocity( b, x, y );
- setcolor( MaxColor );
- circle( (int)s.x, (int)s.y, b->radius ); /* put new body on screen */
- for ( i = 0; i < (body_cnt-1); i++ ) {
- if ( body_pool[ i ] == from ) {
- body_pool[ i ] = body_pool[ i+1 ];
- body_pool[ i+1 ] = from; /* shift other elements of array down */
- } /* ... to fill gap left by absorbed body */
- } /* pointer to be eliminated now at top */
- free( body_pool[ --body_cnt ] ); /* free top pointer in array */
- body_pool[ body_cnt ] = NULL; /* set pointer to NULL to prevent confusion */
- return;
- }
-
- void apply_force( BODY *b, BODY *from )
- { /* Adjusts velocity vector of BODY b given the force of gravity from FROM */
- VECTOR_2D d;
- double rs, r, v, scrn_dist;
-
- if ( (b == from) || (b == NULL) || (from == NULL) )
- return;
- d.x = b->world.x - from->world.x;
- d.y = b->world.y - from->world.y; /* d vector now the distance between bodies */
- rs = ( d.x * d.x ) + ( d.y * d.y ); /* finds straight line distance */
- if ( rs != 0.0 ) { /* ... by calculating hypotenuse */
- r = sqrt( rs ); /* now r = straight line distance */
- scrn_dist = MaxX * r / extent_x; /* scrn_dist is in pixels */
- if ( scrn_dist < ( b->radius + from->radius )) {
- collision( b, from ); /* if the circles touch, call collision() */
- } else {
- v = ( from->gmass / rs ) * SECS_PER_TIC; /* v = the change in velocity */
- b->velocity.x += v * d.x / r; /* add to x and y elements of vector */
- b->velocity.y += v * d.y / r;
- }
- }
- return;
- }
-
- void convert( VECTOR_2D *world, VECTOR_2D *screen )
- { /* Converts world coordinates to screen coordinates */
- screen->x = MaxX * ( world->x / extent_x );
- screen->y = MaxY * ( world->y / extent_y );
- return;
- }
-
- void update( BODY *b )
- { /* Updates the position coordinates of BODY b and moves circle on screen */
- VECTOR_2D s;
-
- convert( &b->world, &s );
- if (( s.x < MaxX ) && ( s.x >= 0 ) && ( s.y < MaxY ) && ( s.y >= 0 )) {
- setcolor( trail_color ); /* erase old circle */
- circle( (int)s.x, (int)s.y, b->radius );
- }
- b->world.x += b->velocity.x * SECS_PER_TIC; /* update world coord. */
- b->world.y += b->velocity.y * SECS_PER_TIC;
- convert( &b->world, &s );
- if (( s.x < MaxX ) && ( s.x >= 0 ) && ( s.y < MaxY ) && ( s.y >= 0 )) {
- setcolor( MaxColor ); /* draw new circle */
- circle( (int)s.x, (int)s.y, b->radius );
- }
- return;
- }
-
- void print_tick( void )
- { /* Prints day:hour in upper left corner */
- static unsigned int tick = 0;
-
- setcolor( BLACK );
- gprintf(1, 1, "%4d:%2u ", (tick / 24), (tick % 24) ); /* erase old one */
- tick++;
- setcolor( MaxColor );
- gprintf(1, 1, "%4d:%2u ", (tick / 24), (tick % 24) ); /* write new one */
- return;
- }
-
- void make_planet( double mass, double px, double py, double vx, double vy )
- { /* creates a new BODY and assigns the next available pointer to it */
- BODY *newplanet;
-
- if ( body_cnt >= POOL_LIMIT )
- return;
- newplanet = calloc( 1, sizeof( BODY ) );
- set_mass( newplanet, mass );
- set_position( newplanet, px, py );
- set_velocity( newplanet, vx, vy );
- body_pool[ body_cnt++ ] = newplanet;
- return;
- }
-
- char move_view( void )
- { /* interprets keystrokes and moves view space accordingly */
- char r = 0;
- int i;
-
- if ( !kbhit() )
- return( r );
- switch ( r = getch() ) {
- case 'X' : /* If X or x is pressed then release planet x */
- case 'x' :
- make_planet( 5e23, extent_x, 0, -1600, 2000);
- break;
- case ESC : /* ESC to quit */
- break;
- case 0:
- switch ( getch() ) {
- case 73: /* PgUp */
- extent_x *= 2;
- extent_y *= 2; /* zoom out */
- for ( i = 0; i < body_cnt; i++ ) {
- set_mass( body_pool[i], body_pool[i]->mass ); /* reset radii */
- body_pool[i]->world.x += extent_x/4; /* keep view centered */
- body_pool[i]->world.y += extent_y/4;
- }
- break;
- case 81: /* PgDn */
- extent_x /= 2;
- extent_y /= 2; /* zoom in */
- for ( i = 0; i < body_cnt; i++ ) {
- set_mass( body_pool[i], body_pool[i]->mass ); /* reset radii */
- body_pool[i]->world.x -= extent_x/2; /* keep view centered */
- body_pool[i]->world.y -= extent_y/2;
- }
- break;
- case 72: /* UP arrow */
- for ( i = 0; i < body_cnt; i++ )
- body_pool[i]->world.y += extent_y/4;
- break;
- case 75: /* LEFT arrow */
- for ( i = 0; i < body_cnt; i++ )
- body_pool[i]->world.x += extent_x/4;
- break;
- case 77: /* RIGHT arrow */
- for ( i = 0; i < body_cnt; i++ )
- body_pool[i]->world.x -= extent_x/4;
- break;
- case 80: /* DOWN arrow */
- for ( i = 0; i < body_cnt; i++ )
- body_pool[i]->world.y -= extent_y/4;
- break;
- } /* end of extended char switch */
- drawborder(); /* clear screen and redraw border */
- } /* end of simple char switch */
- return( r );
- }
-
- void big_bang( void )
- { /* Main loop of simulation */
- int i, j;
-
- do {
- print_tick();
- for ( i = 0; i < body_cnt; i++ ) {
- for ( j = 0; j < body_cnt; j++ ) /* apply to each pair of bodies */
- apply_force( body_pool[ i ], body_pool[ j ] );
- }
- for ( i = 0; i < body_cnt; i++ ) /* update locations */
- update( body_pool[ i ] );
- } while ( move_view() != ESC );
- return; /* quit if ESC was pressed */
- }
-
- void main( void )
- {
- initialize();
- drawborder();
-
- make_planet( 5.9e24, 7.5e8, 7.5e8, 12.5, 0 );
- make_planet( 7.4e22, 7.5e8, 11.3e8, -1020, 0 );
- big_bang();
-
- closegraph();
- }
-